import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { ChangeDetectionStrategy, Component, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpClient, HttpClientModule, HttpContext, HttpErrorResponse, HttpHeaders, HttpParams, HttpResponse } from '@angular/common/http';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { Observable, of, throwError } from 'rxjs';
import { formatSize } from '../core/api';
import { UploadModule } from './upload.module';
import { UploadComponent } from './upload.component';
import { UploadFileStatusEnum, UploadStatusEnum } from './interface';

@Component({
  template: `
    <lv-upload
      lvUrl="/sails/api/v1/upload"
      lvAutoUpload
      lvAccept="image/jpeg,image/png"
      [lvSize]="size"
      [lvDisabled]="disabled"
      [lvMultiple]="multiple"
      [lvType]="type"
      [(lvFiles)]="files"
    ></lv-upload>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
class TestUploadComponent {
  size = 'default';
  multiple = false;
  type = 'select';
  disabled = false;
  files = [
    {
      key: '1',
      name: 'xxx.png',
      size: 10000,
      status: UploadFileStatusEnum.SUCCESS,
    },
    {
      key: '2',
      name: 'yyy.png',
      size: 10,
      status: UploadFileStatusEnum.PROGRESS,
    },
    {
      key: '3',
      name: 'zzz.png',
      size: 1000,
      status: UploadFileStatusEnum.ERROR,
    },
    {
      key: '4',
      name: 'aaa.png',
      size: 1000,
      status: UploadFileStatusEnum.READY,
    },
  ];
}

describe('UploadComponent', () => {
  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      declarations: [TestUploadComponent],
      imports: [UploadModule, HttpClientModule],
      teardown: { destroyAfterEach: false },
    }).compileComponents();
  }));

  describe('render', () => {
    let component: TestUploadComponent;
    let fixture: ComponentFixture<TestUploadComponent>;
    const renderComp = fakeAsync(para => {
      for (const [key, value] of Object.entries(para)) {
        component[key] = value;
      }
      fixture.detectChanges();
      tick(1500);
      fixture.detectChanges();
      expect(fixture).toMatchSnapshot();
    });

    beforeEach(() => {
      fixture = TestBed.createComponent(TestUploadComponent);
      component = fixture.componentInstance;
    });

    it('with lvSize large', () => {
      renderComp({ size: 'large' });
    });

    it('with lvMultiple', () => {
      renderComp({ multiple: true });
    });

    it('with lvType drag', () => {
      renderComp({ type: 'drag' });
    });

    it('with lvDisabled', () => {
      renderComp({ disabled: true });
    });
  });
});

@Component({
  template: `
    <div style="width: 368px">
      <lv-upload
        (lvChange)="change($event)"
        lvUrl="/sails/api/v1/upload"
        lvAutoUpload
        lvAccept="image/jpeg,image/png"
        lvMultiple
        [(lvFiles)]="files"
      ></lv-upload>
    </div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
class TestUploadFuncComponent {
  @ViewChild(UploadComponent, { static: true }) lvUpload: UploadComponent;
  files;
  constructor() {}
  ngOnInit() {
    this.files = [
      {
        key: '-1',
        name: 'xxx.png',
        size: 10000,
        fileSize: formatSize(10000),
        status: UploadFileStatusEnum.SUCCESS,
      },
    ];

    this.lvUpload.setUploadStatus(UploadStatusEnum.COMPLETE);
  }
}

export class HttpClientMock {
  request(
    method: string,
    url: string,
    options: {
      body?: any;
      headers?:
        | HttpHeaders
        | {
            [header: string]: string | string[];
          };
      context?: HttpContext;
      observe?: 'body';
      params?:
        | HttpParams
        | {
            [param: string]: string | number | boolean | ReadonlyArray<string | number | boolean>;
          };
      reportProgress?: boolean;
      responseType: 'arraybuffer';
      withCredentials?: boolean;
    }
  ): Observable<ArrayBuffer> {
    return of(new ArrayBuffer(0));
  }
}

describe('UploadFuncComponent', () => {
  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      declarations: [TestUploadFuncComponent],
      imports: [UploadModule, CommonModule, HttpClientModule, NoopAnimationsModule],
      teardown: { destroyAfterEach: true },
      providers: [
        {
          provide: HttpClient,
          useClass: HttpClientMock,
        },
      ],
    }).compileComponents();
  }));

  describe('function', () => {
    let fixture: ComponentFixture<TestUploadFuncComponent>;
    let instance: UploadComponent;
    let httpService: HttpClient;
    const detectChange = () => {
      fixture.detectChanges();
      tick(500);
      fixture.detectChanges();
      tick(500);
      fixture.detectChanges();
      tick(500);
    };

    beforeEach(fakeAsync(() => {
      fixture = TestBed.createComponent(TestUploadFuncComponent);
      detectChange();
      httpService = fixture.debugElement.injector.get(HttpClient);
      instance = fixture.componentInstance.lvUpload;
      global.URL.createObjectURL = jest.fn(() => 'test.txt');
    }));

    const uploadFile = (fileName: string) => {
      const file = new File(['test'], fileName, { type: 'text/plain' });
      const change = new InputEvent('change');
      jest.spyOn(change, 'target', 'get').mockReturnValue({ files: [file] } as any);
      document.querySelector('input').dispatchEvent(change);
      detectChange();
      expect(fixture).toMatchSnapshot();
    };

    it('upload', fakeAsync(() => {
      jest.spyOn(httpService, 'request').mockReturnValue(of(new HttpResponse({ status: 200 })));
      uploadFile('test1.txt');
      jest.spyOn(httpService, 'request').mockReturnValue(throwError(new HttpErrorResponse({ status: 500 })));
      uploadFile('test2.txt');
      const remove = () => {
        document.querySelector('.lv-fileupload-list div:nth-child(1) i.lv-icon-color-state').dispatchEvent(new Event('click'));
        detectChange();
        expect(fixture).toMatchSnapshot();
      };
      remove();
      jest.spyOn(httpService, 'request').mockReturnValue(of(new HttpResponse({ status: 200 })));
      document.querySelector('.lv-fileupload-cell-error i:nth-child(5)').dispatchEvent(new Event('click'));
      detectChange();
      expect(fixture).toMatchSnapshot();
      remove();
      instance.lvBeforeUpload = jest.fn(() => false);
      uploadFile('test3.txt');
      instance.lvBeforeUpload = jest.fn(() => true);
      instance.lvBeforeSuccess = jest.fn(() => false);
      uploadFile('test4.txt');
    }));

    // TODO: 解决用例报错问题
  });
});
